/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: framebuffer driver
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-06-03     JasonHu           Init
 */

#define NX_LOG_NAME "framebuffer"
#include <base/log.h>

#ifdef CONFIG_NX_DRIVER_FRAMEBUFFER

#include <base/page.h>
#include <base/driver.h>
#include <base/malloc.h>
#include <base/debug.h>

#include <drvfw/framebuffer.h>

#include <drivers/vbe.h>

#define DRV_NAME "framebuffer driver"
#define DRV_VERSION "0.1"

#define DEV_NAME "fb0"

#define VBE_INFO_ADDR  (NX_Phy2Virt(VBE_BASE_INFO_ADDR))
#define VBE_MODE_ADDR  (NX_Phy2Virt(VBE_BASE_MODE_ADDR))

NX_PRIVATE NX_FramebufferInfo fb0Info;

NX_PRIVATE NX_Error FB_Control(struct NX_Device *device, NX_U32 cmd, void *arg)
{
    NX_FramebufferInfo * info;
    NX_FramebufferInfo * fbInfo;

    fbInfo = device->extension;
    NX_ASSERT(fbInfo);

    switch (cmd)
    {
    case NX_FRAMEBUFFER_CMD_GETINFO:
        info = (NX_FramebufferInfo *) arg;
        if (info) {
            info->bitsPerPixel = fbInfo->bitsPerPixel;
            info->bytesPerScanLine = fbInfo->bytesPerScanLine;
            info->xResolution = fbInfo->xResolution;
            info->yResolution = fbInfo->yResolution;
            info->phyBasePtr = fbInfo->phyBasePtr;
        }
        break;
    default:
        return NX_EINVAL;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error FB_Mappable(struct NX_Device *device, NX_Size length, NX_U32 prot, NX_Addr * outPhyAddr)
{
    NX_FramebufferInfo * fbInfo;
    
    fbInfo = device->extension;
    NX_ASSERT(fbInfo);

    if (!fbInfo->phyBasePtr)
    {
        return NX_ENORES;
    }

    if (length > fbInfo->bytesPerScanLine * fbInfo->yResolution)
    {
        return NX_EPERM;
    }

    *outPhyAddr = fbInfo->phyBasePtr;

    return NX_EOK;
}

NX_PRIVATE NX_DriverOps FB_DriverOps = {
    .control    = FB_Control,
    .mappable = FB_Mappable,
};

NX_PRIVATE void FB_DriverInit(void)
{
    NX_Device *device;

    struct VbeInfoBlock * vbeInfo = (struct VbeInfoBlock *)VBE_INFO_ADDR;
    struct VbeModeInfoBlock * modeInfo = (struct VbeModeInfoBlock *)VBE_MODE_ADDR;

    /* check vbe version */
    if (vbeInfo->vbeVeision < 0x0200) {
        NX_LOG_E("vbe: version %x not supported!", vbeInfo->vbeVeision);
        return;
    }

    fb0Info.bitsPerPixel = modeInfo->bitsPerPixel;
    fb0Info.bytesPerScanLine = modeInfo->bytesPerScanLine;
    fb0Info.xResolution = modeInfo->xResolution;
    fb0Info.yResolution = modeInfo->yResolution;
    fb0Info.phyBasePtr = modeInfo->phyBasePtr;

    NX_Driver *driver = NX_DriverCreate(DRV_NAME, NX_DEVICE_TYPE_SCREEN, 0, &FB_DriverOps);
    if (driver == NX_NULL)
    {
        NX_LOG_E("create driver failed!");
        return;
    }

    if (NX_DriverAttachDevice(driver, DEV_NAME, &device) != NX_EOK)
    {
        NX_LOG_E("attach device %s failed!", DEV_NAME);
        NX_DriverDestroy(driver);
        return;
    }

    device->extension = &fb0Info;

    if (NX_DriverRegister(driver) != NX_EOK)
    {
        NX_LOG_E("register driver %s failed!", DRV_NAME);
        NX_DriverDetachDevice(driver, DEV_NAME);
        NX_DriverDestroy(driver);
        return;
    }
}

NX_PRIVATE void FB_DriverExit(void)
{
    NX_DriverCleanup(DRV_NAME);
}

NX_DRV_INIT(FB_DriverInit);
NX_DRV_EXIT(FB_DriverExit);

#endif /* CONFIG_NX_DRIVER_FRAMEBUFFER */
